home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
Amiga Format CD 43
/
Amiga Format CD43 (1999)(Future Publishing)(GB)(Track 1 of 2)[!][issue 1999-09].iso
/
-serious-
/
programming
/
other
/
python-1.52
/
demo
/
chatserver.py
next >
Wrap
Text File
|
1999-06-14
|
12KB
|
394 lines
#
# Multiuser Chat Server. (6-apr-96) by Irmen de Jong.
# Extended by Frank Niessink
#
# Implements a mud-like "chat" server to which multiple users can connect,
# by simple telnet. Some simple commands are supported.
# Feelings are supported, but not fully. No adverbs yet.
#
# Usage: python chatserv.py [hostname] [portnumber]
#
import select
import string
import sys
import time
import socket
WelcomeMsg = "\r\nTELNET CHAT SERVICE...\r\n"
class connection:
def __init__(self,sock,address,name=None):
self.sock=sock
self.myname=name
self.address=address
def __del__(self):
self.sock.close()
self.sock=None
def setname(self,name):
self.myname=name
def name(self):
return self.myname
def send(self,msg):
self.sock.send(msg)
def senderr(self,errmsg):
self.sock.send(mkerr(errmsg))
def sendnote(self,msg):
self.sock.send(mknote(msg))
# The following list contain all open connections.
connections=[]
#Feeling types:
SIMP=0; DEFA=1; PERS=2; QUAD=3; PREV=4; PHYS=5; DEUX=6; SHRT=7
#feelings.. taken from LPC mud soul. Not yet fully supported.
feelings = {
"hmm": [0,0," hmm$ \nHOW \nAT"," at"],
"burp": [1,["rudely"],""," at"],
"wink": [1,["suggestively"],""," at"],
"smile": [1,["happily"],""," at"],
"point": [1,0,""," at"],
"grin": [1,["evilly"],""," at"],
"laugh": [1,0,""," at"],
"nod": [1,["solemnly"],""," at"],
"wave": [1,["happily"],""," at"],
"cackle": [1,["with glee"],""," at"],
"chuckle": [1,["politely"],""," at"],
"bow": [1,0,""," to"],
"glare": [1,["stonily"],""," at"],
"giggle": [1,["merrily"],""," at"],
"groan": [1,0,""," at"],
"grunt": [1,0,""," at"],
"growl": [1,0,""," at"],
"snarl": [1,0,""," at"],
"recoil": [1,["with fear"],""," from"],
"moan": [1,0,""," at"],
"howl": [1,["in pain"],""," at"],
"puke": [1,0,""," on"],
"sneeze": [1,["loudly"],""," at"],
"spit": [1,0,""," on"],
"stare": [1,0,""," at"],
"whistle": [1,["appreciatively"],""," at"],
"applaud": [1,0,""," at"],
"agree": [1,0,""," with"],
"disagree": [1,0,""," with"],
"fart": [1,0,""," at"],
"dance": [1,0,""," with"],
"purr": [1,0,""," at"],
"listen": [1,0,""," to"],
"apologize": [1,0,""," to"],
"complain": [1,0,""," about"],
"beg": [2,0," beg$ \nHOW"," beg$ \nWHO for mercy \nHOW"],
"shake": [2,0," shake$ \nYOUR head \nHOW"," shake$ hands with \nWHO \nHOW"],
"grimace": [0,0," \nHOW make$ an awful face \nAT"," at"],
"stomp": [2,0," stomp$ \nYOUR foot \nHOW"," stomp$ on \nPOSS foot \nHOW"],
"snigger": [1,["jeeringly"],""," at"],
"watch": [3,["carefully"]," watch the surroundings \nHOW", " watches the surroundings \nHOW"," watch \nWHO \nHOW"," watches \nWHO \nHOW",],
"scratch": [3,["on the head"]," scratch \nMYself \nHOW"," scratches \nMYself \nHOW"," scratch \nWHO \nHOW"," scratches \nWHO \nHOW",],
"tap": [2,["impatiently",0,"on the shoulder"]," tap$ \nYOUR foot \nHOW"," tap$ \nWHO \nWHERE"],
"curse": [2,0," curse$ \nWHAT \nHOW"," curse$ \nWHO \nHOW"],
"sing": [0,0," \nHOW sing$ \nWHAT \nAT"," to"],
"mumble": [0,0," mumble$ \nWHAT \nHOW \nAT"," to"],
"scream": [0,["loudly"]," scream$ \nWHAT \nAT \nHOW"," at"],
"yell": [0,["in a high pitched voice"]," yell$ \nHOW \nWHAT \nAT"," at"],
"hide": [0,0," hide$ \nHOW behind \nWHO"],
"pounce": [5,["playfully"],""],
"kneel": [0,0," \nHOW fall$ on \nYOUR knees \nAT"," in front of"],
"roll": [0,["to the ceiling"]," roll$ \nYOUR eyes \nHOW"],
"boggle": [0,0," boggle$ \nHOW at the concept"],
"cheer": [7,["enthusiastically"],""],
"wiggle": [0,0," wiggle$ \nYOUR bottom \nHOW"],
"flip": [0,0," flip$ \nHOW head over heels"],
"cry": [6,0," cry \nHOW"," cries \nHOW"],
"sob": [7,0,""],
"sweat": [7,0,""],
"gurgle": [7,0,""],
"grumble": [7,0,""],
"die": [6,0," fall \nHOW down and play dead"," falls \nHOW to the ground, dead"],
"stumble": [7,0,""],
"bounce": [7,0,""],
"yawn": [7,0,""],
"sulk": [7,["in the corner"],""],
"strut": [7,["proudly"],""],
"sniff": [7,0,""],
"snore": [7,0,""],
"snicker": [7,0,""],
"smirk": [7,0,""],
"jump": [0,["in aggravation"] ," jump$ up and down \nHOW"],
"fume": [7,0 ,""],
"faint": [7,0,""],
"shrug": [7,0,""],
"pout": [7,0,""],
"hiccup": [7,0,""],
"frown": [7,0,""],
"gasp": [7,["in astonishment"],""],
"think": [7,["carefully"],""],
"ponder": [7,["over some problem"],""],
"clap": [7,0,""],
"sigh": [7,0,""],
"cough": [7,["noisily"],""],
"shiver": [7,["from the cold"],""],
"blush": [6,0," blush \nHOW"," blushes \nHOW"]
}
# exit : graceful exit
def exit(code):
global connections
del connections
raise SystemExit,code
# stripcrlf: strip CR/LF or LF from string
def stripcrlf(str):
if len(str)>2:
if (str[-2:]=='\r\n') or (str[-2:]=='\n\r'): return str[:-2]
if len(str)>1:
if str[-1]=='\n': return str[:-1]
return str
# broadcast: send a msg to all connections, except self.
def broadcast(self_idx,msg):
i=0
for c in connections:
if i!=self_idx: c.sock.send(msg)
i=i+1
# shutdown: close connection & send msg to quitter & others
def shutdown(i,msg,brmsg):
if brmsg: broadcast(i,brmsg)
connections[i].send(msg)
log(i,"QUIT")
del connections[i]
# log: log message from connection #i (to screen)
def log(i,msg):
if connections[i].name():
print msg+'\t'+connections[i].name()+'\t'+`connections[i].address`,time.ctime(time.time())
else:
print msg+"\t<???>\t"+`connections[i].address`,time.ctime(time.time())
# mkerr: convert string into error string
# mknote: convert string into note string
def mkerr(msg):
return "** ERROR: "+msg+" **\r\n"
def mknote(msg):
return "** NOTE: "+msg+" **\r\n"
# do_feeling: execute feeling 'data'. i=index of active socket.
def do_feeling(i,data):
line=string.split(data)
feeling=string.lower(line[0])[1:]
if len(line)>1:
args=line[1:]
else:
args=None
if feeling in feelings.keys():
adv=""
if feelings[feeling][1]:
adv=' '+feelings[feeling][1][0]
connections[i].send("You "+feeling+adv+".\r\n")
if feeling[-2:]=='ch' or feeling[-1]=='s' or feeling[-2:]=='sh':
feeling=feeling+'e'
if feeling[-1]=='y':
feeling=feeling[:-1]+"ie"
broadcast(i,connections[i].name()+' '+feeling+'s'+adv+".\r\n")
else:
connections[i].senderr("Unknown feeling")
# do_chatter: broadcast normal chatter
def do_chatter(i, data):
broadcast(i,connections[i].name()+" says: `"+data+"'\r\n")
# do_to: send chatter to only one person
def do_to(i, args):
if args:
destname=args[0]
else:
connections[i].senderr("No destination provided")
return
data=string.join(args[1:])
if destname in map(lambda x:x.name(),connections):
dest=map(lambda x:x.name(),connections).index(destname)
connections[dest].send(connections[i].name()+" says to you: `"+data+"'\r\n")
else:
connections[i].senderr("Unknown name")
# do_help: send a help message
def do_help(i, args):
if args and args[0]=="feelings":
connections[i].send("** FEELINGS **\r\n"
"Currently, the following feeling commands have been defined:\r\n")
s=""
fls=feelings.keys()
fls.sort()
for f in fls:
if (len(s)+len(f))>=76:
connections[i].send(s+"\r\n")
s=""
s=s+f+' '
connections[i].send(s+"\n")
else:
connections[i].send("** CHAT SERVICE ONLINE HELP **\r\n"
"All normal text entered is sent to all other people, as if you 'said' it.\r\n"
"Lines starting with ' are a special feelings line. See @help feelings.\r\n"
"Lines starting with @ are regarded as a special command line:\r\n"
"@help topic - help on topic\r\n"
"@to foo bar - say 'bar' to 'foo' only\r\n"
"@who - who's there\r\n"
"@name foo - set your name to 'foo'\r\n"
"@quit - exit chat service\r\n"
"@eject who passwd - eject someone (operator cmd)\r\n"
"@shutdown passwd) - shutdown server (operator cmd)\r\n")
# do_who: show who are there
def do_who(i):
str=""
for c in connections:
str=str+" "+c.name()+"\r\n"
connections[i].send(str)
# do_name: change name
def do_name(i,args):
if not args:
connections[i].senderr("No name")
elif len(args[0]) < 2:
connections[i].senderr("Name too short")
elif args[0] in map(lambda x:x.name(),connections):
connections[i].senderr("Name already in use")
else:
broadcast(-1,mknote(connections[i].name()+" now known as: "+args[0]))
connections[i].setname(args[0])
# do_eject: eject someone, need password for this
def do_eject(i, args):
if not password:
connections[i].senderr("Sysop commands not enabled")
elif len(args) != 2:
connections[i].senderr("Usage: @eject name password")
elif not (args[0] in map(lambda x:x.name(), connections)):
connections[i].senderr("Unknown name")
elif args[1] != password:
connections[i].senderr("Wrong password")
else:
dest=map(lambda x:x.name(),connections).index(args[0])
shutdown(dest,"You were ejected by "+connections[i].name()+".\n",mknote(connections[dest].name()+" WAS EJECTED BY "+connections[i].name()))
# do_shutdown: shut down the server (needs passwd)
def do_shutdown(i, args):
if not password:
connections[i].senderr("Sysop commands not enabled")
elif len(args) != 1:
connections[i].senderr("Usage: @shutdown password")
elif args[0] != password:
connections[i].senderr("Wrong password")
else:
broadcast(-1,mknote("Server shutdown by "+connections[i].name()))
log(i,"SHUTDOWN");
exit(0)
# do_command: execute command 'data'. i=index of active socket.
def do_command(i,data):
line=string.split(data)
cmd=string.lower(line[0])[1:]
args=line[1:]
if cmd=='name':
do_name(i,args)
elif cmd=="who":
do_who(i)
elif cmd=="quit":
shutdown(i,"Quitting chat service.\n",mknote(connections[i].name()+" LEFT"))
elif cmd=="to":
do_to(i, args)
elif cmd=="help":
do_help(i, args)
elif cmd=="eject":
do_eject(i, args)
elif cmd=="shutdown":
do_shutdown(i, args)
else:
connections[i].senderr("Bad CMD, try @help")
def serverloop(host,port):
global feelings
print "\nStarting telnet chat service on",host,port,"..."
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
s.bind(host, port)
s.listen(1)
while 1:
socklist=map(lambda x:x.sock, connections)
ins,outs,exs = select.select(socklist+[s],[],[],5)
for sock in ins:
if sock==s:
sock,addr=s.accept()
connections.append(connection(sock,addr))
sock.send(WelcomeMsg+"Enter your name: ")
else:
i = socklist.index(sock)
data=sock.recv(512)
if data:
data=string.strip(stripcrlf(data))
# If we don't have a name yet, read the name.
if not connections[i].name():
if len(data)<2:
shutdown(i,mkerr("Name too short, try again"),None)
elif data in map(lambda x:x.name(),connections):
shutdown(i,mkerr("Name already in use, try again"),None)
else:
connections[i].setname(data)
broadcast(i,mknote(connections[i].name()+" ARRIVES"))
connections[i].sendnote("Ok, enter chatter or @help for help")
log(i,"CONNECT")
elif data:
if data[0]=='\'':
do_feeling(i,data)
elif data[0]=='@':
do_command(i,data)
else:
do_chatter(i,data)
if sock.fileno()>0: sock.send("> ")
else:
# EOF read or closed connection.
shutdown(i,"Quitting chat service.\r\n",mknote(connections[i].name()+" LEAVES"))
def main(host,port):
global connections, password
password=raw_input("Enter password (or just enter for no passwd): ");
serverloop(host,port)
print "Server shut down."
del connections # close all connections
if __name__=='__main__':
argc=len(sys.argv)
if argc==1:
main(socket.gethostname(),9999)
elif argc==2:
main(socket.gethostname(),eval(sys.argv[1]))
elif argc==3:
main(sys.argv[1],eval(sys.argv[2]))
else:
print "Usage: python",sys.argv[0],"[hostname] [portnumber]"